#pragma once

#include <linux/rbtree_types.h>
#include <linux/hrtimer_types.h>
#include <asm/rwonce.h>
#include <linux/ktime.h>

/*
 * Mode arguments of xxx_hrtimer functions:
 *
 * HRTIMER_MODE_ABS		- Time value is absolute
 * HRTIMER_MODE_REL		- Time value is relative to now
 * HRTIMER_MODE_PINNED		- Timer is bound to CPU (is only considered
 *				  when starting the timer)
 * HRTIMER_MODE_SOFT		- Timer callback function will be executed in
 *				  soft irq context
 * HRTIMER_MODE_HARD		- Timer callback function will be executed in
 *				  hard irq context even on PREEMPT_RT.
 */
enum hrtimer_mode
{
    HRTIMER_MODE_ABS = 0x00,
    HRTIMER_MODE_REL = 0x01,
    HRTIMER_MODE_PINNED = 0x02,
    HRTIMER_MODE_SOFT = 0x04,
    HRTIMER_MODE_HARD = 0x08,

    HRTIMER_MODE_ABS_PINNED = HRTIMER_MODE_ABS | HRTIMER_MODE_PINNED,
    HRTIMER_MODE_REL_PINNED = HRTIMER_MODE_REL | HRTIMER_MODE_PINNED,

    HRTIMER_MODE_ABS_SOFT = HRTIMER_MODE_ABS | HRTIMER_MODE_SOFT,
    HRTIMER_MODE_REL_SOFT = HRTIMER_MODE_REL | HRTIMER_MODE_SOFT,

    HRTIMER_MODE_ABS_PINNED_SOFT = HRTIMER_MODE_ABS_PINNED | HRTIMER_MODE_SOFT,
    HRTIMER_MODE_REL_PINNED_SOFT = HRTIMER_MODE_REL_PINNED | HRTIMER_MODE_SOFT,

    HRTIMER_MODE_ABS_HARD = HRTIMER_MODE_ABS | HRTIMER_MODE_HARD,
    HRTIMER_MODE_REL_HARD = HRTIMER_MODE_REL | HRTIMER_MODE_HARD,

    HRTIMER_MODE_ABS_PINNED_HARD = HRTIMER_MODE_ABS_PINNED | HRTIMER_MODE_HARD,
    HRTIMER_MODE_REL_PINNED_HARD = HRTIMER_MODE_REL_PINNED | HRTIMER_MODE_HARD,
};

/*
 * Values to track state of the timer
 *
 * Possible states:
 *
 * 0x00		inactive
 * 0x01		enqueued into rbtree
 *
 * The callback state is not part of the timer->state because clearing it would
 * mean touching the timer after the callback, this makes it impossible to free
 * the timer from the callback function.
 *
 * Therefore we track the callback state in:
 *
 *	timer->base->cpu_base->running == timer
 *
 * On SMP it is possible to have a "callback function running and enqueued"
 * status. It happens for example when a posix timer expired and the callback
 * queued a signal. Between dropping the lock which protects the posix timer
 * and reacquiring the base lock of the hrtimer, another CPU can deliver the
 * signal and rearm the timer.
 *
 * All state transitions are protected by cpu_base->lock.
 */
#define HRTIMER_STATE_INACTIVE 0x00
#define HRTIMER_STATE_ENQUEUED 0x01

/* Soft interrupt function to run the hrtimer queues: */
extern void hrtimer_run_queues(void);

/* Basic timer operations: */
extern void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
                                   u64 range_ns, const enum hrtimer_mode mode);

static inline ktime_t hrtimer_get_expires(const struct hrtimer *timer)
{
    return timer->node.expires;
}

static inline ktime_t hrtimer_get_softexpires(const struct hrtimer *timer)
{
    return timer->_softexpires;
}

extern bool hrtimer_active(const struct hrtimer *timer);

/**
 * hrtimer_start - (re)start an hrtimer
 * @timer:	the timer to be added
 * @tim:	expiry time
 * @mode:	timer mode: absolute (HRTIMER_MODE_ABS) or
 *		relative (HRTIMER_MODE_REL), and pinned (HRTIMER_MODE_PINNED);
 *		softirq based mode is considered for debug purpose only!
 */
static inline void hrtimer_start(struct hrtimer *timer, ktime_t tim,
                                 const enum hrtimer_mode mode)
{
    hrtimer_start_range_ns(timer, tim, 0, mode);
}

/**
 * hrtimer_is_queued - check, whether the timer is on one of the queues
 * @timer:	Timer to check
 *
 * Returns: True if the timer is queued, false otherwise
 *
 * The function can be used lockless, but it gives only a current snapshot.
 */
static inline bool hrtimer_is_queued(struct hrtimer *timer)
{
    /* The READ_ONCE pairs with the update functions of timer->state */
    return !!(READ_ONCE(timer->state) & HRTIMER_STATE_ENQUEUED);
}

/*
 * Helper function to check, whether the timer is running the callback
 * function
 */
bool hrtimer_callback_running(struct hrtimer *timer);

/* Initialize timers: */
extern void hrtimer_setup(struct hrtimer *timer, enum hrtimer_restart (*function)(struct hrtimer *),
                          clockid_t clock_id, enum hrtimer_mode mode);
int hrtimer_try_to_cancel(struct hrtimer *timer);

/* Bootup initialization: */
extern void hrtimers_init(void);

ktime_t hrtimer_cb_get_time(struct hrtimer *timer);

static inline void hrtimer_set_expires_range_ns(struct hrtimer *timer, ktime_t time, u64 delta)
{
    timer->_softexpires = time;
    timer->node.expires = ktime_add_safe(time, ns_to_ktime(delta));
}

u64 hrtimer_forward_now(struct hrtimer *timer, ktime_t interval);

static inline void hrtimer_add_expires_ns(struct hrtimer *timer, u64 ns)
{
    timer->node.expires = ktime_add_ns(timer->node.expires, ns);
    timer->_softexpires = ktime_add_ns(timer->_softexpires, ns);
}

static inline void hrtimer_add_expires(struct hrtimer *timer, ktime_t time)
{
    timer->node.expires = ktime_add_safe(timer->node.expires, time);
    timer->_softexpires = ktime_add_safe(timer->_softexpires, time);
}

static inline s64 hrtimer_get_expires_tv64(const struct hrtimer *timer)
{
    return timer->node.expires;
}

extern u64 hrtimer_get_next_event(void);

void hrtimer_start_expires(struct hrtimer *timer, enum hrtimer_mode mode);

static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time)
{
    timer->node.expires = time;
    timer->_softexpires = time;
}

u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval);
int hrtimer_cancel(struct hrtimer *timer);

void destroy_hrtimer_on_stack(struct hrtimer *timer);

extern int schedule_hrtimeout(ktime_t *expires, const enum hrtimer_mode mode);
void hrtimer_sleeper_start_expires(struct hrtimer_sleeper *sl,
                                   enum hrtimer_mode mode);
extern void hrtimer_setup_sleeper_on_stack(struct hrtimer_sleeper *sl, clockid_t clock_id,
					   enum hrtimer_mode mode);
